package com.example.redislearn.example.relationship.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.example.redislearn.constant.RedisKeyConstant;
import com.example.redislearn.constant.RelationShipEnum;
import com.example.redislearn.example.relationship.service.RelationshipService;
import org.redisson.Redisson;
import org.redisson.api.RSet;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;

@Service
public class RelationshipServiceImpl implements RelationshipService {

    @Resource
    private Redisson redisson;

    @Override
    public boolean focusOnAndTakeOff(String userId, String followingId) {
        RSet<String> watchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + userId);
        boolean contains = watchSet.contains(followingId);
        return contains ? takeOff(userId, followingId) : focusOn(userId, followingId);
    }

    /**
     * 取关
     *
     * @param userId      用户id
     * @param followingId 被关注的用户id
     * @return 是否成功
     */
    private boolean takeOff(String userId, String followingId) {
        RSet<String> watchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + userId);
        boolean result = watchSet.remove(followingId);
        RSet<String> fansSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.FANS_SET_PREFIX + followingId);
        return result && fansSet.remove(userId);
    }

    /**
     * 关注
     *
     * @param userId      用户id
     * @param followingId 被关注的用户id
     * @return 是否成功
     */
    private boolean focusOn(String userId, String followingId) {
        RSet<String> watchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + userId);
        boolean result = watchSet.add(followingId);
        RSet<String> fansSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.FANS_SET_PREFIX + followingId);
        return result && fansSet.add(userId);
    }

    @Override
    public RelationShipEnum checkRelationShip(String aUserId, String bUserId) {
        if (StringUtils.pathEquals(aUserId, bUserId)) {
            return RelationShipEnum.ONESELF;
        }
        RSet<String> aWatchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + aUserId);
        boolean contains = aWatchSet.contains(bUserId);
        RSet<String> bWatchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + bUserId);
        boolean contains1 = bWatchSet.contains(aUserId);
        if (contains && contains1) {
            return RelationShipEnum.FRIEND;
        } else if (contains) {
            return RelationShipEnum.IDOL;
        } else if (contains1) {
            return RelationShipEnum.FANS;
        }
        return RelationShipEnum.STRANGER;
    }

    @Override
    public Set<String> getWatchSet(String userId) {
        RSet<String> watchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + userId);
        return watchSet.readAll();
    }

    @Override
    public Set<String> getFansSet(String userId) {
        RSet<String> fansSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.FANS_SET_PREFIX + userId);
        return fansSet.readAll();
    }

    @Override
    public Set<String> getIntersectionWatchSet(String aUserId, String bUserId) {
        RSet<String> aWatchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + aUserId);
        RSet<String> bWatchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + bUserId);
        // redisson 不支持在操作不同分片上的set集合, redisson pro 可以
        // aWatchSet.readIntersection(bWatchSet.toArray(new String[0]));
        Set<String> aSet = aWatchSet.readAll();
        Set<String> bSet = bWatchSet.readAll();
        aSet.retainAll(bSet);
        return aSet;
    }

    @Override
    public Set<String> getIntersectionFansSet(String aUserId, String bUserId) {
        RSet<String> aFansSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.FANS_SET_PREFIX + aUserId);
        RSet<String> bFansSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.FANS_SET_PREFIX + bUserId);
        Set<String> aSet = aFansSet.readAll();
        Set<String> bSet = bFansSet.readAll();
        aSet.retainAll(bSet);
        return aSet;
    }

    @Override
    public Set<String> recommendWatchSet(String userId) {
        Set<String> result = new HashSet<>();
        RSet<String> watchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + userId);
        watchSet.stream().filter(obj -> !obj.equals(userId)).forEach(obj -> {
            RSet<String> fansSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.FANS_SET_PREFIX + obj);
            fansSet.forEach(fansId -> {
                RSet<String> fansWatchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + fansId);
                result.addAll(fansWatchSet.readAll());
            });
        });
        result.removeAll(watchSet);
        return result;
    }


    @Override
    public Set<String> randomRecommendWatchSet(String userId, Integer num) {
        RSet<String> recommendSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.RECOMMEND_SET_PREFIX + userId);
        Set<String> random = recommendSet.random(num);
        if (CollUtil.isNotEmpty(random)) {
            return random;
        }
        // 获取关注列表
        RSet<String> watchSet = redisson.getSet(RedisKeyConstant.RELATIONSHIP.WATCH_SET_PREFIX + userId);
        // 获取关注列表用户的id
        Set<String> readAll = watchSet.readAll();
        if (CollUtil.isEmpty(readAll)) {
            return null;
        }
        // 差集
        Set<String> readDiff = watchSet.readDiff(readAll.toArray(new String[0]));
        // 推荐列表
        recommendSet.addAll(readDiff);
        return recommendSet.random(num);
    }
}
